/**  
* @Title: CacheConfig.java
* @Package com.lemon.conf.redis
* @Description: (一句话阐述):
* @author Ran_cc
* @date 2018年5月17日
* @version V1.0  
*/
package com.lemon.conf.redis;

import java.lang.reflect.Method;
import java.time.Duration;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lemon.conf.DruidDBConfig;
import com.lemon.conf.redis.custom.CustomizedRedisCacheManager;


@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
	

	private static Logger log = LoggerFactory.getLogger(DruidDBConfig.class);

	@Bean
	public KeyGenerator KeyGenerator() {
		return new KeyGenerator() {
			@Override
			public Object generate( Object target, Method method, Object ... params ) {
				StringBuilder sb = new StringBuilder();
				sb.append(target.getClass().getName());
				sb.append(method.getName());
				for (Object obj : params) {
					sb.append(obj.toString());
				}
				System.out.println("override-key:" + sb.toString());
				return sb.toString();
			}
		};
	}


	/*@Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {  
        RedisCacheManager manager = new RedisCacheManager(redisTemplate);  
        manager.setUsePrefix(true);  
        RedisCachePrefix cachePrefix = new RedisPrefix("prefix");  
        manager.setCachePrefix(cachePrefix);  
        // 整体缓存过期时间  
        manager.setDefaultExpiration(3600L);  
        // 设置缓存过期时间。key和缓存过期时间，单位秒  
        Map<String, Long> expiresMap = new HashMap<>();  
        expiresMap.put("user", 1000L);  
        manager.setExpires(expiresMap);  
        return manager;  
	}*/

	@Resource( name = "redisTemplate" )
	private RedisOperations<String, String> redisOperations;

	/**
	 * <b>Description:<em><blockquote>
	 * 管理cacheManager <br>
	 * 因为这是用springboot2.x 版本整合的redis , 所以网上一般都是1.x的不适用 . 改变好了好多构造
	 * </blockquote></em></b>
	 * @Title: cacheManager
	 * @param connectionFactory
	 * @return
	 * @author Ran_cc
	 * @date 2018年5月28日
	 */
	@Bean
	CacheManager cacheManager( RedisConnectionFactory connectionFactory ) {

		System.out.println("springCache配置Redis...");
	        //初始化一个RedisCacheWriter  
		RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);

	        //设置CacheManager的值序列化方式为JdkSerializationRedisSerializer,但其实RedisCacheConfiguration默认就是使用StringRedisSerializer序列化key，JdkSerializationRedisSerializer序列化value,所以以下注释代码为默认实现  
		//		ClassLoader loader = this.getClass().getClassLoader();
		//		JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
		//		RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
		//		RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);
		RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();

		//设置默认超过期时间是30秒    
		//特别注意 defaultCacheConfig.entryTtl(Duration.ofSeconds(30));网上的这段代码一定要接收返回值配置的过期时间才有效
		defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(72000));
		//初始化RedisCacheManager  
		//		CustomizedRedisCacheManager cacheManager = new CustomizedRedisCacheManager(redisCacheWriter, defaultCacheConfig);
		CustomizedRedisCacheManager cacheManager = new CustomizedRedisCacheManager(redisCacheWriter, defaultCacheConfig, redisOperations);
		//		RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
		return cacheManager;
	}

	@Bean
	public RedisTemplate<String, String> redisTemplate( RedisConnectionFactory factory ) {
		StringRedisTemplate template = new StringRedisTemplate(factory);
		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		template.setValueSerializer(jackson2JsonRedisSerializer);
		template.afterPropertiesSet();
		return template;
	}

    @Bean
    public CacheErrorHandler errorHandler() {
        return new RedisCacheErrorHandler();
    }
    private static class RedisCacheErrorHandler extends SimpleCacheErrorHandler {

        @Override
		public void handleCacheGetError( RuntimeException exception, Cache cache, Object key ) {
			log.error("handleCacheGetError key = {}, value = {}", key, cache);
			log.error("cache get error", exception);
		}

		@Override
        public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
            log.error("handleCachePutError key = {}, value = {}", key, cache);
            log.error("cache put error", exception);
        }

        @Override
        public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
            log.error("handleCacheEvictError key = {}, value = {}", key, cache);
            log.error("cache evict error", exception);
        }

        @Override
        public void handleCacheClearError(RuntimeException exception, Cache cache) {
            log.error("handleCacheClearError value = {}", cache);
            log.error("cache clear error", exception);
        }
    }


}  	